/*
* Copyright (c) 2007 by Kirill Kolodyazhniy.
* See the file "license.terms" for information on usage and redistribution.
*/

#ifndef M_MESH_H
#define M_MESH_H

#include "resource.h"
#include "aabb.h"
#include "types.h"
#include "submesh.h"
#include "refptr.h"
#include "xmlreader.h"
#include "vertexdeclaration.h"
#include "hardwarebuffer.h"

#include <vector>
#include <string>

namespace Engine
{
	/**
	*   Class for operating with mesh.
	*/
	class Mesh: public Resource
	{
		typedef std::vector<RefPtr<SubMesh, WeakPtrExtension> > SubMeshVector;
	public:

		/**
		*   Constructor.
		*/
		Mesh(const std::string& name);

		/**
		*   Destructor.
		*/
		~Mesh();

		/**
		*   Retrieves the local axis-aligned bounding box for this object.
        */
		const AABB& GetBoundingBox(void) const;

        /**
		*   Gets the number of sub meshes which comprise this mesh.
        */
		unsigned int GetNumSubMeshes(void) const;

		/**
		*   Gets a SubMesh by index.
		*/
		SubMeshPtr GetSubMesh(unsigned int index);

		/**
		*   Load mesh from file.
		*/
		virtual void Load();

		/**
		*   Free memory, but not delete.
		*/
		virtual void UnLoad();

		/**
		*   Get vertex data.
		*/
		const HardWareBufferPtr GetVertexData() const;

		/**
		*   Returns vertex count.
		*/
		unsigned int GetVertexCount() const;

		/**
		*   Returns mesh vertex declaration.
		*/
		const VertexDeclarationPtr GetVertexDeclaration() const;

	private:

		/**
		*   Read xml definition
		*	<mesh>
		*		<submeshes count = "2">
		*		  <submesh material = "mat2.material">
		*			<faces count = "354">
		*			  <face v1="0" v2="1" v3="2" />
		*			</faces>
		*		  </submesh>
		*		  <submesh material = "mat2.material">
		*			<faces count = "354">
		*			  <face v1="0" v2="1" v3="2" />
		*			</faces>
		*		  </submesh>
		*		</submeshes>
		*		<vertices count = "233" positions = "true" normals = "true" color = "true" texture_coords = "1" >
		*			<vertex>
		*				<position x="-25.1193" y="63.8602" z="24.5164" />
		*				<normal x="-0.417703" y="0.665569" z="-0.6185" />
		*				<texcoord0 u="0.428711" v="0.766078" />
		*				<color r="0.181657" g="-0.605809" b="-0.774594" a="1.0"/>
		*			</vertex>
		*		</vertices>
		*	</mesh>
		*/
		void ReadMesh(RefPtr<XmlElement> pElem,const std::string& name);

		/**
		*   Read xml definitions for mesh data.
		*/
		void ReadObjects(RefPtr<XmlElement> pElem,const std::string& name);

		/**
		*   Read xml vertex declaration.
		*/
		void ReadVertexDeclaration(const std::string& name, const std::string& value);

		/**
		*   Read xml vertex data.
		*/
		void ReadVertexData(RefPtr<XmlElement> pElem,const std::string& name);

		/**
		*   Read xml vertex elements.
		*/
		void ReadVertex(RefPtr<XmlElement> pElem,const std::string& name);

		/**
		*   Write raw data to vertex buffer.
		*/
		template<class T>
		void WriteRawData(T* source, unsigned int count, unsigned int offsetInVertex, unsigned int offsetInBuffer);

		/**
		*   Update bounding box with new vertex.
		*/
		void UpdateBBox(Vector3<REAL> vertex);

	private:

		AABB m_boundingBox;
		RefPtr<VertexDeclaration,WeakPtrExtension> m_vertexDeclaration;
		unsigned int m_vertexCount;
		RefPtr<HardWareBuffer,WeakPtrExtension> m_vertexData; // vertex data used in all submeshes.
		SubMeshVector m_subMeshes; // submeshes

		unsigned int m_currentVertexIndex;
		unsigned int m_vertexElementOffset;
		void*        m_vertexDataPtr;
	};
	/**********************************************************************************************************/
	template<class T>
	void Mesh::WriteRawData(T* source, unsigned int count, unsigned int offsetInVertex, unsigned int offsetInBuffer)
	{
		offsetInBuffer *= m_vertexDeclaration->GetVertexSize(0);
		memcpy(static_cast<unsigned char*>(m_vertexDataPtr) + offsetInBuffer + offsetInVertex, source, count * sizeof(T));
	}
	/**********************************************************************************************************/


	/**
	*   Factory for creating mesh resources.
	*/
	class MeshFactory: public ResourceFactory
	{
	public:
		MeshFactory():m_type("Mesh"){}

		/**
		*   Returns the factory type.
		*/
		virtual const std::string& GetType() const {return m_type;}

		/**
		*   Creates a new object.
		*   @param name Name of the object to create
		*   @return	An object created by the factory. The type of the object depends on	the factory.
		*/
		virtual Mesh* CreateInstance( const std::string& name );

	private:

		std::string m_type;
	};

}

#endif
